/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.tnmk.worktime.gui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.joda.time.DateTime;
import org.tnmk.worktime.config.AppConfig;
import org.tnmk.worktime.gui.list.LabelItemRenderer;
import org.tnmk.worktime.gui.tbl.ButtonEditor;
import org.tnmk.worktime.gui.tbl.ButtonRenderer;
import org.tnmk.worktime.gui.tbl.ButtonType;
import org.tnmk.worktime.gui.utils.JComboUtils;
import org.tnmk.worktime.gui.utils.JTableUtils;
import org.tnmk.worktime.service.SessionService;
import org.tnmk.worktime.service.dao.entry.SessionType;
import org.tnmk.worktime.service.dao.entry.SpendEntry;
import org.tnmk.worktime.service.model.SessionModel;
import org.tnmk.worktime.utils.NumUtils;
import org.tnmk.worktime.utils.TimeUtils;

/**
 * @author Administrator
 * Most of the change in detail, we just need to change on view.
 * When user select to edit other session, it will get data from view and update to StorageBuffer.
 */
public class DetailController {

  private static final int COL_SPEND_ID = 2;
  private final MainFrame frame;
  private SessionModel viewModel;//editing model. If editing currentSession, must be updated continuously from service.
  private SessionService service;//no setter

  public DetailController(MainFrame frame) {
    this.frame = frame;
  }

  void initDetailData(SessionService service) {
    this.service = service;
    this.viewModel = service.getCurrentSession();
  }

  void initGUI() {
    frame.jcboSessionType.setRenderer(new LabelItemRenderer());
    JComboUtils.setModel(frame.jcboSessionType, SessionType.class.getEnumConstants());
    
    final JTable jtbl = frame.getJtblSpend();
    //Focus configuration
    JTableUtils.disableFocusOnColumn(jtbl, COL_SPEND_ID);
    JTableUtils.setNextFocusByColumn(jtbl);
    
    jtbl.setIntercellSpacing(new Dimension(0, 0));    
    JTableUtils.addCellEditorListenerToAllColumns(jtbl, new CellEditorListener() {
      @Override
      public void editingCanceled(ChangeEvent e) {
        updateSpendEntry();
      }

      @Override
      public void editingStopped(ChangeEvent e) {
        updateSpendEntry();
      }
    });
    JTableUtils.setNumberFormat(jtbl, 0);

    ButtonEditor deleteSpendCellEditor = new ButtonEditor(ButtonType.DELETE.toString());
    deleteSpendCellEditor.setButtonAction(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (jtbl.isEditing()) {//Must adding this line to avoid ArrayIndexOutOfBoundException when deleting a row
          jtbl.getCellEditor().stopCellEditing();
        }
        int slcRowIndex = jtbl.getSelectedRow();
        deleteSpendEntry(slcRowIndex);
      }
    });
    TableColumnModel colModel = jtbl.getColumnModel();
    TableColumn editCol = colModel.getColumn(COL_SPEND_ID);
    editCol.setCellRenderer(new ButtonRenderer(ButtonType.DELETE.toString()));
    editCol.setCellEditor(deleteSpendCellEditor);
    editCol.setMaxWidth(16);
    editCol.setPreferredWidth(16);

    JTableUtils.setWidth(jtbl, 0, 75);
  }

  /**
   * This method just remove spend from session in View layer
   *
   * @param viewModel
   * @param spendId
   */
  public void updateInTime() {
    String timeStr = frame.jtxtInTime.getText();
    DateTime time = AppConfig.toDateTime(timeStr);
    viewModel.setInTime(time);
    service.recalculateModel(viewModel);
    service.addOrUpdateSessionAndSpends(viewModel);
    frame.toGUI();
  }
  public void updateSessionType() {
    Object val = frame.jcboSessionType.getSelectedItem();
    SessionType type = (SessionType)val;
    viewModel.setType(type);
    service.recalculateModel(viewModel);
    service.addOrUpdateSessionAndSpends(viewModel);
    frame.toGUI();
//    LogUtils.printCaller(this,"Val: "+val,1);
  }
  public void updateOutTime() {
    String timeStr = frame.jtxtOutTime.getText();
    DateTime time = AppConfig.toDateTime(timeStr);
    viewModel.setOutTime(time);
    service.recalculateModel(viewModel);
    service.addOrUpdateSessionAndSpends(viewModel);
    frame.toGUI();
  }

  public void toDetailGUI() {
    toDetailGUI(true, true);
  }

  public void toDetailGUI(boolean refreshEditable, boolean refreshSpendGUI) {
    if (refreshEditable) {
      frame.jtxtInTime.setText(AppConfig.toString(viewModel.getInTime()));
      frame.jtxtOutTime.setText(AppConfig.toString(viewModel.getOutTime()));
      frame.jtxtNote.setText(viewModel.getNote());
    }
    frame.jtxtExpectOutTime.setText(AppConfig.toString(viewModel.getExpectedOutTime()));
    frame.jtxtMustWorkHoursToday.setText(TimeUtils.toTimeAndDecimalString(viewModel.getMustWorkHoursToday()));
    frame.jtxtBreakBeginTime.setText(AppConfig.toString(viewModel.getBreakBeginTime()));
    frame.jtxtBreakEndTime.setText(AppConfig.toString(viewModel.getBreakEndTime()));

    frame.jtxtWorkedHours.setText(TimeUtils.toTimeAndDecimalString(viewModel.getWorkedHours()));
    frame.jtxtWeekWorkedHoursHistory.setText(TimeUtils.toTimeAndDecimalString(viewModel.getWeekWorkedHours()));
    frame.jtxtMonthWorkedHours.setText(TimeUtils.toTimeAndDecimalString(viewModel.getMonthWorkedHours()));
    frame.jcboSessionType.setSelectedItem(viewModel.getType());
    
    if (refreshSpendGUI) {
      toSpendGUI();
    }
  }

  void toSpendGUI() {
    DefaultTableModel tblModel = (DefaultTableModel) frame.getJtblSpend().getModel();
    tblModel.setRowCount(0);//clear all rows
    List<SpendEntry> spends = viewModel.getMoneySpend().getItems();
    for (SpendEntry entry : spends) {
      Object[] row = toSpendRow(entry);
      tblModel.addRow(row);
    }
    frame.jtxtSumSpend.setText(NumUtils.toString(viewModel.getSumSpend()));
    frame.jtxtSumWeekSpend.setText(NumUtils.toString(viewModel.getSumWeekSpend()));
    frame.jtxtSumMonthSpend.setText(NumUtils.toString(viewModel.getSumMonthSpend()));
  }

  public SessionModel getViewModel() {
    return viewModel;
  }

  public void setViewModel(SessionModel viewModel) {
    this.viewModel = viewModel;
  }

  /**
   * Note: this method only work if data in JTblSpend is same as JTblSpend.Model
   *
   * @param entry
   * @return
   */
  protected Object[] toSpendRow(SpendEntry entry) {
    int i = frame.getJtblSpend().getRowCount();
    return new Object[]{entry.getCost(), entry.getNote(), entry.getId()};
  }

  protected SpendEntry toSpendEntryFromTableRow(int row) {
    Object valA = frame.getJtblSpend().getValueAt(row, 0);
    Object valB = frame.getJtblSpend().getValueAt(row, 1);
    Object valC = frame.getJtblSpend().getValueAt(row, 2);
    Long cost = NumUtils.toLong(valA);
    Integer id = NumUtils.toLong(valC).intValue();
    SpendEntry e = new SpendEntry();
    e.setId(id);
    e.setCost(cost);
    e.setNote((String) valB);
    return e;
  }

  public void addSpendEntry() {
    DefaultTableModel tblModel = (DefaultTableModel) frame.getJtblSpend().getModel();
    SpendEntry newEntry = new SpendEntry();
    service.addSpend(this.viewModel, newEntry);
    tblModel.addRow(toSpendRow(newEntry));
  }

  private void deleteSpendEntry(int slcRowIndex) {
    JTable jtbl = frame.getJtblSpend();
//    if (jtbl.isEditing()) {//Must adding this line to avoid ArrayIndexOutOfBoundException when deleting a row
//      jtbl.getCellEditor().stopCellEditing();
//    }
    DefaultTableModel model = (DefaultTableModel) jtbl.getModel();
    SpendEntry spend = toSpendEntryFromTableRow(slcRowIndex);
    ControllerHelper.removeSpendInSessionViewModel(viewModel, spend.getId());//delete on viewModel
    model.removeRow(slcRowIndex);//delete on view
  }

  void updateSpendEntry() {
    JTable jtbl = frame.getJtblSpend();
    int row = jtbl.getSelectedRow();
    if (row < 0) {
      row = jtbl.getEditingRow();
    }
    SpendEntry entry = toSpendEntryFromTableRow(row);
    int spendIndex = ControllerHelper.findSpendInSessionViewModel(viewModel, entry.getId());
    viewModel.getMoneySpend().getItems().set(spendIndex, entry);
    service.addOrUpdateSessionAndSpends(viewModel);
    
//    jtbl.changeSelection(0, 0, false, false);
//    JTableUtils.editNextCell(jtbl, jtbl.getColumnCount() - 1);//not focus on last column
  }
}
